/*
 * @(#) $Header$
 *
 * Copyright (C) 2007  Forklabs Daniel Léonard
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package ca.forklabs.media.jai.operator;

import java.awt.RenderingHints;
import java.awt.image.RenderedImage;
import java.awt.image.renderable.ParameterBlock;
import java.awt.image.renderable.RenderableImage;
import java.util.Collection;
import javax.media.jai.JAI;
import javax.media.jai.OperationDescriptor;
import javax.media.jai.ParameterBlockJAI;
import javax.media.jai.ParameterListDescriptor;
import javax.media.jai.registry.CollectionRegistryMode;
import javax.media.jai.registry.RenderableCollectionRegistryMode;
import ca.forklabs.media.jai.CollectionDescriptor;

/**
 * Class {@code PeriodicShift3DDescriptor} is an {@link OperationDescriptor}
 * describing the <em>periodicshift</em> operation. This operation is an
 * extension of JAI operator <em>periodicshift</em>.
 * <p>
 * The <em>periodicshift</em> operation takes three parameters: the shift in
 * <em>x</em> (the width of the image), the shift in <em>y</em> (the height of
 * the image) and the shift in <em>z</em> (the depth of the image).
 * <p>
 * It is assumed that all the images in the collection are of the same size,
 * otherwise surprising results may occurs especially with default values.
 *
 * <table border=1>
 * <caption>Resource List</caption>
 * <tr><th>Name</th>        <th>Value</th></tr>
 * <tr><td>GlobalName</td>  <td>PeriodicShift3D</td></tr>
 * <tr><td>LocalName</td>   <td>PeriodicShift3D</td></tr>
 * <tr><td>Vendor</td>      <td>ca.forklabs.media.jai.opimage</td></tr>
 * <tr><td>Description</td> <td>Periodic shift on a 3D image</td></tr>
 * <tr><td>DocURL</td>      <td>n/a</td></tr>
 * <tr><td>Version</td>     <td>$Version$</td></tr>
 * <tr><td>Arg0Desct</td>   <td>The displacement in the X direction (optional)</td></tr>
 * <tr><td>Arg1Desct</td>   <td>The displacement in the Y direction (optional)</td></tr>
 * <tr><td>Arg2Desct</td>   <td>The displacement in the Z direction (optional)</td></tr>
 * </table>
 *
 * <table border=1>
 * <caption>Parameter List</caption>
 * <tr><th>Name</th>     <th>Class Type</th>   <th>Default Value</th></tr>
 * <tr><td>shiftX</td>   <td>Integer</td>      <td>sourceWidth/2</td>
 * <tr><td>shiftY</td>   <td>Integer</td>      <td>sourceHeight/2</td>
 * <tr><td>shiftZ</td>   <td>Integer</td>      <td>sourceDepth/2</td>
 * </table>
 *
 * @author   <a href="mailto:forklabs at gmail.com?subject=ca.forklabs.media.jai.operator.PeriodicShift3DDescriptor">Daniel Léonard</a>
 * @version $Revision$
 */
public class PeriodicShift3DDescriptor extends CollectionDescriptor {

//---------------------------
// Class variables
//---------------------------

   /** <em>serialVersionUID</em>. */
   private static final long serialVersionUID = 846175619312050342L;

   /** The name of this operator. */
   public static final String NAME = "PeriodicShift"; //$NON-NLS-1$

   /** The name of the <em>shiftX</em> parameter. */
   public static final String SHIFT_X_PARAMETER_NAME = "shiftX"; //$NON-NLS-1$
   /** The name of the <em>shiftY</em> parameter. */
   public static final String SHIFT_Y_PARAMETER_NAME = "shiftY"; //$NON-NLS-1$
   /** The name of the <em>shiftZ</em> parameter. */
   public static final String SHIFT_Z_PARAMETER_NAME = "shiftZ"; //$NON-NLS-1$

   /** The index in the parameter block of the <em>shifX</em> parameter. */
   public static final int SHIFT_X_PARAMETER_INDEX = 0;
   /** The index in the parameter block of the <em>shifY</em> parameter. */
   public static final int SHIFT_Y_PARAMETER_INDEX = 1;
   /** The index in the parameter block of the <em>shifZ</em> parameter. */
   public static final int SHIFT_Z_PARAMETER_INDEX = 2;

   /**
    * The resource strings that provide the general documentation and specify
    * the parameter list for this operation.
    */
   private static final String[][] RESOURCES =
   {
      { "GlobalName",  PeriodicShift3DDescriptor.NAME, }, //$NON-NLS-1$
      { "LocalName",   PeriodicShift3DDescriptor.NAME, }, //$NON-NLS-1$
      { "Vendor",      "ca.forklabs.media.jai.opimage", }, //$NON-NLS-1$ //$NON-NLS-2$
      { "Description", PeriodicShift3DDescriptor.getDescription(), }, //$NON-NLS-1$
      { "DocURL",      "n/a", }, //$NON-NLS-1$ //$NON-NLS-2$
      { "Version",     "$Version$", }, //$NON-NLS-1$ //$NON-NLS-2$
      { "arg0Desc",    PeriodicShift3DDescriptor.getArg0Description(), }, //$NON-NLS-1$
      { "arg1Desc",    PeriodicShift3DDescriptor.getArg1Description(), }, //$NON-NLS-1$
      { "arg2Desc",    PeriodicShift3DDescriptor.getArg2Description(), }, //$NON-NLS-1$
   };

   /** The supported modes. */
   private static final String[] SUPPORTED_MODES =
   {
      CollectionRegistryMode.MODE_NAME,
      RenderableCollectionRegistryMode.MODE_NAME,
   };

   /** The name of the source, use default. */
   private static final String[] SOURCE_NAMES = null;

   /** The type of source for each mode. */
   private static final Class<?>[][] SOURCE_CLASSES = new Class<?>[][]
   {
      { Collection.class, },
      { Collection.class, },
   };

   /** The parameter list descriptor for all modes. */
   private static final ParameterListDescriptor PARAMETER_LIST_DESCRIPTOR = new CollectionDescriptor.EmptyParameterListDescriptor() {

      @Override
      public int getNumParameters() {
         int num_parameters = 3;
         return num_parameters;
         }

      @Override
      public Class<?>[] getParamClasses() {
         Class<?>[] clazzes = new Class<?>[] {
            Integer.class,
            Integer.class,
            Integer.class,
            };
         return clazzes;
         }

      @Override
      public Object getParamDefaultValue(String name) {
      // the default values are calculated
         Object value = null;
         return value;
         }

      @Override
      public Object[] getParamDefaults() {
      // the default values are calculated
         Object[] defaults = new Object[] {
            this.getParamDefaultValue(PeriodicShift3DDescriptor.SHIFT_X_PARAMETER_NAME),
            this.getParamDefaultValue(PeriodicShift3DDescriptor.SHIFT_Y_PARAMETER_NAME),
            this.getParamDefaultValue(PeriodicShift3DDescriptor.SHIFT_Z_PARAMETER_NAME),
            };
         return defaults;
         }

      @Override
      public String[] getParamNames() {
         String[] names = {
            PeriodicShift3DDescriptor.SHIFT_X_PARAMETER_NAME,
            PeriodicShift3DDescriptor.SHIFT_Y_PARAMETER_NAME,
            PeriodicShift3DDescriptor.SHIFT_Z_PARAMETER_NAME,
            };
         return names;
         }

      @Override
      public boolean isParameterValueValid(String name, Object value) {
      // all values are valid
         boolean is_valid = true;
         return is_valid;
         }

      };

   /** Description of the parameters. */
   private static final ParameterListDescriptor[] PARAMETER_LIST_DESCRIPTORS = new ParameterListDescriptor[]
   {
      PeriodicShift3DDescriptor.PARAMETER_LIST_DESCRIPTOR,
      PeriodicShift3DDescriptor.PARAMETER_LIST_DESCRIPTOR,
   };


//---------------------------
// Constructor
//---------------------------

   /**
    * Constructor.
    */
   public PeriodicShift3DDescriptor() {
      super(RESOURCES, SUPPORTED_MODES, SOURCE_NAMES, SOURCE_CLASSES, PARAMETER_LIST_DESCRIPTORS);
      }


//---------------------------
// Overridden methods from javax.media.jai.OperationDescriptorImpl
//---------------------------

   /**
    * Adds the <em>shiftZ</em> parameter if it is missing then makes sure that
    * all the image in the collection are of the same type.
    * @param   mode   the rendering mode.
    * @param   pb   the parameter block.
    * @param   sb   the error message buffer.
    * @return   {@code true} if all is OK, {@code false} otherwise.
    */
   @Override
   @SuppressWarnings("boxing")
   public boolean validateParameters(String mode, ParameterBlock pb, StringBuffer sb) {
      boolean all_ok = super.validateParameters(mode, pb, sb);
      if (false == all_ok) {
         return all_ok;
         }

   // we deal only with the shift_z parameter, the other two
   // will be dealt in the implementation when operator
   // "periodicshift" is called
      Integer shift_z = (Integer) pb.getObjectParameter(PeriodicShift3DDescriptor.SHIFT_Z_PARAMETER_INDEX);
      if (null == shift_z) {
         Collection<?> sources = (Collection<?>) pb.getSource(0);
         int depth = sources.size();
         shift_z = depth / 2;
         pb.set(shift_z, PeriodicShift3DDescriptor.SHIFT_Z_PARAMETER_INDEX);
         }

      return all_ok;
      }


//---------------------------
// Class methods
//---------------------------

   /**
    * Creates and fills a new parameter block.
    * @param   mode   the rendering mode.
    * @param   source   the source image.
    * @param   shift_x   the displacement in <em>x</em>.
    * @param   shift_y   the displacement in <em>y</em>.
    * @param   shift_z   the displacement in <em>z</em>.
    * @return   a new parameter block.
    */
   protected static ParameterBlockJAI createParameterBlock(String mode, Object source, int shift_x, int shift_y, int shift_z) {
      String name = PeriodicShift3DDescriptor.NAME;
      ParameterBlockJAI pb = new ParameterBlockJAI(name, mode);
      if (null != source) {
         pb.addSource(source);
         }
      pb.setParameter(PeriodicShift3DDescriptor.SHIFT_X_PARAMETER_NAME, shift_x);
      pb.setParameter(PeriodicShift3DDescriptor.SHIFT_Y_PARAMETER_NAME, shift_y);
      pb.setParameter(PeriodicShift3DDescriptor.SHIFT_Z_PARAMETER_NAME, shift_z);
      return pb;
      }

   /**
    * Performs the operation on a collection of rendered image.
    * @param   sources   the source images.
    * @param   shift_x   the displacement in <em>x</em>.
    * @param   shift_y   the displacement in <em>y</em>.
    * @param   shift_z   the displacement in <em>z</em>.
    * @param   hints    the rendering hints, may be {@code null}.
    * @return   the resulting images.
    */
   @SuppressWarnings("unchecked")
   public static Collection<RenderedImage> createCollection(Collection<RenderedImage> sources, int shift_x, int shift_y, int shift_z, RenderingHints hints)  {
      String mode = CollectionRegistryMode.MODE_NAME;
      String name = PeriodicShift3DDescriptor.NAME;
      ParameterBlockJAI parameter_block = PeriodicShift3DDescriptor.createParameterBlock(mode, sources, shift_x, shift_y, shift_z);
      Collection<RenderedImage> sinks = JAI.createCollection(name, parameter_block, hints);
      return sinks;
      }

   /**
    * Performs the operation on a collection of renderable image.
    * @param   sources   the source images.
    * @param   shift_x   the displacement in <em>x</em>.
    * @param   shift_y   the displacement in <em>y</em>.
    * @param   shift_z   the displacement in <em>z</em>.
    * @param   hints    the rendering hints, may be {@code null}.
    * @return   the resulting images.
    */
   @SuppressWarnings("unchecked")
   public static Collection<RenderableImage> createRenderableCollection(Collection<RenderableImage> sources, int shift_x, int shift_y, int shift_z, RenderingHints hints)  {
      String mode = RenderableCollectionRegistryMode.MODE_NAME;
      String name = PeriodicShift3DDescriptor.NAME;
      ParameterBlockJAI parameter_block = PeriodicShift3DDescriptor.createParameterBlock(mode, sources, shift_x, shift_y, shift_z);
      Collection<RenderableImage> sinks = JAI.createRenderableCollection(name, parameter_block, hints);
      return sinks;
      }


//---------------------------
// External resources methods
//---------------------------

   /**
    * Gets the description of this operation.
    * @return   the description message.
    */
   protected static String getDescription() {
      String key = Resources.PERIODIC_SHIFT_3D_DESCRIPTION;
      String message = Resources.getLocalizedString(key);
      return message;
      }

   /**
    * Gets the description for the first argument, the displacement in
    * <em>x</em>.
    * @return   the description message.
    */
   protected static String getArg0Description() {
      String key = Resources.PERIODIC_SHIFT_3D_ARG0_DESCRIPTION;
      String message = Resources.getLocalizedString(key);
      return message;
      }

   /**
    * Gets the description for the second argument, the displacement in
    * <em>y</em>.
    * @return   the description message.
    */
   protected static String getArg1Description() {
      String key = Resources.PERIODIC_SHIFT_3D_ARG1_DESCRIPTION;
      String message = Resources.getLocalizedString(key);
      return message;
      }

   /**
    * Gets the description for the third argument, the displacement in
    * <em>z</em>.
    * @return   the description message.
    */
   protected static String getArg2Description() {
      String key = Resources.PERIODIC_SHIFT_3D_ARG2_DESCRIPTION;
      String message = Resources.getLocalizedString(key);
      return message;
      }

   }
